Skip to content

SREP-1107: Added a new 'managed-scripts' subcommand to osdctl promote#855

Open
Nikokolas3270 wants to merge 1 commit intoopenshift:masterfrom
Nikokolas3270:SREP-1107
Open

SREP-1107: Added a new 'managed-scripts' subcommand to osdctl promote#855
Nikokolas3270 wants to merge 1 commit intoopenshift:masterfrom
Nikokolas3270:SREP-1107

Conversation

@Nikokolas3270
Copy link
Copy Markdown
Contributor

@Nikokolas3270 Nikokolas3270 commented Feb 25, 2026

This also contains the following changes:

  • 'pko' promote subcommand removed as it was no longer applying to any saas file
  • '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd
  • Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as 'gopkg.in/yaml.v3' is no longer supported
  • Now interacting with Git repo with 'github.com/go-git/go-git/v6' lib rather than running shell commands
  • Code has been factorized

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 25, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Feb 25, 2026

@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This also contains the following changes:

  • 'pko' promote subcommand removed as it was no longer applying to any saas file
  • '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd
  • Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as 'gopkg.in/yaml.v3' is no longer supported
  • Now interacting with Git repo with 'github.com/go-git/go-git/v6' lib rather than running shell commands
  • Code has been factorized

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 25, 2026

Walkthrough

Registry-driven service promotion replaces the previous git/app-interface-centric workflows, adding utilities for local repo handling and service registry, removing the PKO command, and introducing a new managedscripts promote subcommand. Dynatrace and Saas flows were refactored to use the registry and kyaml-based YAML traversal.

Changes

Cohort / File(s) Summary
Command wiring
cmd/promote/cmd.go
Replaced pko subcommand with managedscripts subcommand registration.
ManagedScripts command
cmd/promote/managedscripts/managed_scripts.go, cmd/promote/managedscripts/managed_scripts_test.go
Added new managedscripts promote command and tests; implements promote callbacks and flags (--gitHash, --appInterfaceDir, --namespaceRef).
PKO removal
cmd/promote/pko/pko.go
Removed PKO promote command and related promotion logic.
Dynatrace refactor
cmd/promote/dynatrace/dynatrace.go, cmd/promote/dynatrace/dt_utils.go, cmd/promote/dynatrace/utils_test.go
Switched Dynatrace flow from app-interface/git-based discovery to registry-driven service lookup; added kyaml usage, renamed GeModulesNames→GetModulesNames, converted command to RunE, and replaced listing/promote helpers.
SaaS refactor
cmd/promote/saas/saas.go, cmd/promote/saas/utils.go, cmd/promote/saas/utils_test.go
Removed legacy saas utilities; introduced validateSaasServiceFilePath, promoteCallbacks, e2e name/URL helpers, reworked flags (--serviceId, --hotfix, --appInterfaceDir) and promote flow to use ServicesRegistry.
Removed pathutil tests & code
cmd/promote/pathutil/pathutil.go, cmd/promote/pathutil/pathutil_test.go
Deleted path derivation implementation and its tests.
Removed git/app-interface modules & tests
cmd/promote/git/app_interface.go, cmd/promote/git/app_interface_test.go, cmd/promote/git/service_repo.go, cmd/promote/git/service_repo_test.go
Deleted prior git/app-interface abstractions, service_repo logic, and associated tests.
New utils: app-interface clone & git repo
cmd/promote/utils/app_interface_clone.go, cmd/promote/utils/git_repo.go
Added AppInterfaceClone for local clone validation/branch/commit operations and Repo utilities (clone, ResolveHash, FormattedLog) using go-git.
New utils: service model & registry
cmd/promote/utils/service.go, cmd/promote/utils/services_registry.go
Added Service/Application/CodeComponent models, PromoteCallbacks interface and DefaultPromoteCallbacks, Service.Promote implementation, and ServicesRegistry for discovering/loading services from app-interface.
Test infra and suites added
cmd/promote/utils/test_tools.go, cmd/promote/utils/*_test.go, cmd/promote/saas/saas_test.go, cmd/promote/utils/service_test.go, cmd/promote/utils/services_registry_test.go, cmd/promote/utils/app_interface_clone_test.go, cmd/promote/utils/managedscripts test
Added comprehensive test tooling and Ginkgo/Gomega suites exercising new utils, registry, service promotion flows, and AppInterfaceClone behavior.
Docs updated/added
docs/README.md, docs/osdctl_promote.md, docs/osdctl_promote_managedscripts.md, docs/osdctl_promote_saas.md
Replaced package command documentation with managedscripts; updated saas docs to reflect new flags and behavior.
Dependencies
go.mod
Added github.com/go-git/go-git/v5 and numerous indirect dependencies required by go-git and new code.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning Pull request deletes 1,777 lines of tests while adding 824 lines of untested production code across five new files with no corresponding test files. Create comprehensive test files for all new utilities following table-driven test patterns with proper setup/cleanup, error paths, edge cases, and meaningful assertions.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a new 'managed-scripts' subcommand to the promote command.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Stable And Deterministic Test Names ✅ Passed No dynamic test names containing generated IDs, timestamps, pod names, UUIDs, or other non-deterministic values were found in any test files. All test cases use static, stable test descriptions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Feb 25, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Nikokolas3270

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 25, 2026
@Nikokolas3270 Nikokolas3270 marked this pull request as draft February 25, 2026 09:20
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 25, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Feb 25, 2026

@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This also contains the following changes:

  • 'pko' promote subcommand removed as it was no longer applying to any saas file
  • '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd
  • Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as 'gopkg.in/yaml.v3' is no longer supported
  • Now interacting with Git repo with 'github.com/go-git/go-git/v6' lib rather than running shell commands
  • Code has been factorized

Summary by CodeRabbit

Release Notes

  • New Features

  • Added new osdctl promote managedscripts command for promoting managed-scripts repository commits

  • Improvements

  • Refactored promotion system to use registry-based service discovery for more reliable configuration lookup

  • Enhanced commit messages with monitoring links and test log references

  • Improved error handling and control flow across promotion workflows

  • Removed

  • Removed osdctl promote package command

  • Consolidated OSD/HCP flag behavior into unified promotion flow

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
cmd/promote/dynatrace/dt_utils.go (2)

175-178: ⚠️ Potential issue | 🟠 Major

Return immediately when opening the file fails.

The current flow logs the error and continues, which can pass an invalid file handle into downstream processing.

Proposed fix
 	file, err := Open(filename)
 	if err != nil {
-		fmt.Println(err)
+		return fmt.Errorf("failed to open file '%s': %w", filename, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dt_utils.go` around lines 175 - 178, The Open(filename)
call currently prints the error and continues, which can pass an invalid file
handle into downstream code; update the error handling at the Open(...) site so
that on err you return the error (or otherwise abort the function) instead of
just fmt.Println(err). Locate the Open(filename) invocation in dt_utils.go and
modify the surrounding function to propagate the error (or call return)
immediately when err != nil, ensuring downstream code does not receive a
nil/invalid file handle.

142-153: ⚠️ Potential issue | 🟠 Major

Propagate os.ReadDir failures instead of discarding them.

Read errors are currently ignored at multiple levels, so the promotion can incorrectly continue as if files were scanned successfully.

Proposed fix
-	items, _ := os.ReadDir(dir)
+	items, err := os.ReadDir(dir)
+	if err != nil {
+		return "", fmt.Errorf("failed to read directory '%s': %w", dir, err)
+	}
...
-			subitems, _ := os.ReadDir(subDir)
+			subitems, err := os.ReadDir(subDir)
+			if err != nil {
+				return "", fmt.Errorf("failed to read directory '%s': %w", subDir, err)
+			}
...
-					subitems2, _ := os.ReadDir(subDir2)
+					subitems2, err := os.ReadDir(subDir2)
+					if err != nil {
+						return "", fmt.Errorf("failed to read directory '%s': %w", subDir2, err)
+					}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dt_utils.go` around lines 142 - 153, The os.ReadDir
calls in the nested directory walk (the reads that populate items, subitems and
subitems2) currently ignore their returned errors; change each call to capture
the error (e.g., items, err := os.ReadDir(dir)) and propagate it instead of
discarding it—return the error (or wrap and return) from the enclosing function
(the directory-walking function in dt_utils.go) so failures stop the promotion;
do the same for subitems and subitems2 reads and adjust calling code to handle
the returned error.
cmd/promote/dynatrace/dynatrace.go (1)

73-74: ⚠️ Potential issue | 🟠 Major

Don’t ignore list errors before successful exit.

Both list branches discard the returned error and then exit with success, which can hide real failures from users and automation.

Proposed fix
-					_ = listDynatraceModuleNames(dynatraceConfig)
-					os.Exit(0)
+					if err := listDynatraceModuleNames(dynatraceConfig); err != nil {
+						return fmt.Errorf("failed to list dynatrace modules: %w", err)
+					}
+					return nil
...
-					_ = listServiceIds(servicesRegistry)
-					os.Exit(0)
+					if err := listServiceIds(servicesRegistry); err != nil {
+						return fmt.Errorf("failed to list dynatrace components: %w", err)
+					}
+					return nil

Also applies to: 117-118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dynatrace.go` around lines 73 - 74, The calls to
listDynatraceModuleNames currently discard the returned error and then call
os.Exit(0); change both call sites to capture the error (e.g., err :=
listDynatraceModuleNames(dynatraceConfig)), check if err != nil, print or log
the error to stderr (or use the existing logger) and call os.Exit(1); if no
error, then exit with success. This ensures failures from
listDynatraceModuleNames are surfaced instead of being silently ignored.
🧹 Nitpick comments (5)
cmd/promote/dynatrace/dt_utils.go (1)

108-124: Reset module caches in GetModulesNames before repopulating.

ModulesSlice and ModulesFilesMap are package globals and currently accumulate across repeated calls.

Proposed fix
 func GetModulesNames(baseDir, dir string) ([]string, error) {
+	ModulesSlice = ModulesSlice[:0]
+	ModulesFilesMap = map[string]string{}
+
 	dirGlob := filepath.Join(baseDir, dir)
 	filepaths, err := os.ReadDir(dirGlob)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dt_utils.go` around lines 108 - 124, GetModulesNames
currently appends to package globals ModulesSlice and ModulesFilesMap on each
call; modify GetModulesNames to reset/clear ModulesSlice and reinitialize or
clear ModulesFilesMap at the start of the function (before reading dir entries)
so repeated calls don't accumulate stale data; locate the function
GetModulesNames and add logic to set ModulesSlice = nil (or empty slice) and
ModulesFilesMap = make(map[string]string) (or clear existing entries) before
populating them.
cmd/promote/utils/service.go (4)

42-44: Inconsistent receiver name on yamlDoc.

GetFilePath (line 38) and Save (line 46) use d, but GetName uses s. Use a consistent receiver name across all methods of the same type.

Proposed fix
-func (s *yamlDoc) GetName() string {
-	return s.name
+func (d *yamlDoc) GetName() string {
+	return d.name
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/service.go` around lines 42 - 44, The GetName method for
type yamlDoc uses an inconsistent receiver name `s`; change its receiver to `d`
to match the other methods on yamlDoc (e.g., GetFilePath and Save) so all
methods use the same receiver identifier; update the method signature for
GetName to use `d` and keep the body returning d.name.

413-419: Nit: unconventional blank line in error handling.

The blank line between the call on line 414 and the if err != nil on line 416 breaks the standard Go error-handling idiom. Consider removing it for consistency.

Proposed fix
 	if newHash == "" {
 		newHash, err = repo.GetHeadHash()
-
 		if err != nil {
 			return err
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/service.go` around lines 413 - 419, Remove the extra blank
line in the error handling after calling repo.GetHeadHash so the idiomatic Go
pattern is preserved; specifically, in the block that assigns newHash (the call
to repo.GetHeadHash) and the subsequent if err != nil block, bring the if
directly under the call (no blank line) to keep the standard newHash, err :=
repo.GetHeadHash / if err != nil style used elsewhere.

296-330: Partial failure leaves committed state in the local branch.

Each resourceTemplatePromotion.promote call saves the YAML and commits independently (lines 307, 318). If a later promotion in the loop (line 428) fails, earlier promotions are already committed to the local branch. This is likely acceptable since the user must manually push, but consider documenting this behavior or logging a warning when a partial failure occurs so users know what's been committed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/service.go` around lines 296 - 330, The promote function
(resourceTemplatePromotion.promote) commits each resource independently (uses
callbacks.SetTargetHash, service.Save, service.appInterfaceClone.Commit) which
can leave the local branch partially committed if a later promotion fails;
update the caller that loops over promotions (the code that invokes
resourceTemplatePromotion.promote) to track which promotions succeeded and, on
any subsequent error, emit a clear warning/log that lists the committed resource
relPaths and informs the user that the local branch contains partial commits
requiring manual push/revert; alternatively expose a boolean or error type from
promote indicating "committed" so the caller can build that list before logging
the warning.

21-21: Export the yamlDoc type to match its exported function.

ReadYamlDocFromFile is exported and used externally (e.g., cmd/promote/saas/saas.go:118), but it returns *yamlDoc, which is unexported. This violates Go conventions — external callers cannot declare variables of this type. Export yamlDoc as YamlDoc (or use the lowercase-exported pattern if intentional, but document it).

Additionally, the receiver names for yamlDoc methods are inconsistent: GetFilePath() and Save() use receiver d, while GetName() (line 42) uses receiver s. Standardize on a single receiver name like d.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/service.go` at line 21, The exported function
ReadYamlDocFromFile returns an unexported type yamlDoc which prevents external
callers from using its value; rename the type to exported YamlDoc (or change the
function to return an exported interface) and update all references accordingly
(including ReadYamlDocFromFile's return type and any uses in
cmd/promote/saas/saas.go). Also standardize method receiver names on that type:
change the GetName receiver from 's' to 'd' to match GetFilePath and Save so all
methods on yamlDoc/YamlDoc use a consistent receiver (d). Ensure you update any
imports/usages and tests to the new exported type name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/promote/managedscripts/managed_scripts.go`:
- Around line 30-32: The FilterTargets method currently hardcodes
prodNamespaceRef when calling utils.FilterTargetsContainingNamespaceRef, so the
configured --namespaceRef value is ignored; update
promoteCallbacks.FilterTargets to pass the instance/configured namespace
reference (e.g., c.namespaceRef or the field that holds the CLI flag) into
utils.FilterTargetsContainingNamespaceRef instead of prodNamespaceRef, ensuring
the same change is applied where else prodNamespaceRef is used for target
selection so the runtime flag actually influences filtering.

In `@cmd/promote/saas/saas.go`:
- Line 234: Update the CLI help/example string to use the canonical flag
--serviceId instead of the legacy alias --serviceName: locate the usage text
that currently reads `osdctl promote saas --serviceName <service> --gitHash
<git-hash>` (in the promote command's help/usage output) and replace
`--serviceName` with `--serviceId`; ensure any adjacent help text or examples
referencing the legacy alias are also updated so the example consistently shows
`--serviceId <service>` with `--gitHash <git-hash>`.
- Around line 271-274: Add a fail-fast required check for ops.serviceId before
calling servicesRegistry.GetService to give a clearer error for promote mode:
validate that ops.serviceId is non-empty (e.g., at the start of the promote
handler or before the GetService call), and return a descriptive error if it's
empty (mentioning that --serviceId is required in promote mode). Update the code
path where servicesRegistry.GetService(ops.serviceId) is invoked (reference
ops.serviceId and the promote handler function) to perform this explicit check
and only call GetService when the value is present.
- Around line 38-43: The current logic returns the original filePath when
deploy.yaml is missing, which can register a directory as a service; change the
code to only return valid regular file paths: after checking subFilePath, also
verify that filePath itself is a regular file (use os.Stat(filePath) and
fileInfo.Mode().IsRegular()) before returning it, and if it's a directory or not
a regular file return an empty string (or an error sentinel) so the caller can
skip this SaaS entry; adjust callers of this helper to treat empty string as
"skip invalid entry".

In `@cmd/promote/utils/git_repo.go`:
- Around line 54-89: The code pre-marks commonAncestorCommit.Hash in
visitedHashes before traversal which allows walks that never actually reach
commonAncestorHash to succeed silently; modify the traversal in the function
containing queue, visitedHashes, commit.IsAncestor and the final sb.WriteString
branch so that you either do not pre-populate visitedHashes with
commonAncestorCommit.Hash or (preferably) add a post-traversal check that
verifies visitedHashes contains commonAncestorHash (or that you visited it
during traversal) and return an explicit error if it was not reached; use the
symbols visitedHashes, commonAncestorCommit / commonAncestorHash, queue,
commit.IsAncestor and the final return path to locate where to add the check.

In `@cmd/promote/utils/service.go`:
- Around line 212-229: The function FilterTargetsContainingNamespaceRef
currently uses strings.Contains(visitedNamespaceRef, namespaceRef) which allows
substring matches; change the check to a precise match (e.g., verify the path
ends with the expected segment or exact identifier) by replacing the contains
logic with a more strict comparison such as using strings.HasSuffix or parsing
visitedNamespaceRef into path segments and comparing the final segment to
namespaceRef (operate on visitedNamespaceRef and namespaceRef variables inside
FilterTargetsContainingNamespaceRef so only exact/segment matches are
considered).
- Around line 141-146: The code reads appRelFilePath from
yamlDoc.rootNode.GetString and directly uses
filepath.Join(appInterfaceClone.GetPath(), "data", appRelFilePath) before
calling readApplicationFromFile; sanitize appRelFilePath by running
filepath.Clean, reject absolute paths or any path that resolves outside the
intended base (e.g. contains ".." segments that escape the
appInterfaceClone.GetPath()/data directory), and construct the final path by
joining the cleaned relative path to appInterfaceClone.GetPath()/data then
verifying the resulting path has the base directory as a prefix before calling
readApplicationFromFile.
- Around line 389-394: Iterate deterministically over oldHashToTargetNodes by
collecting its keys into a slice, sorting that slice (using sort.Strings), and
then building resourceTemplatePromotions in the sorted order (instead of ranging
the map directly); update the import block to include "sort". Ensure the same
symbols are used: oldHashToTargetNodes, resourceTemplatePromotions,
resourceTemplatePromotion, and that downstream code that calls promote will now
see a stable commit order.

In `@cmd/promote/utils/services_registry.go`:
- Around line 19-33: The constructor NewServicesRegistry should guard against
nil inputs to avoid panics: at the start of the function check if
appInterfaceClone is nil and if so return a descriptive error (do not call
appInterfaceClone.GetPath()); also check if validateServiceFilePathCallback is
nil and return a descriptive error before invoking it later; update
callers/tests if necessary to pass non-nil values or to handle the returned
error from NewServicesRegistry.
- Around line 36-37: The code silently overwrites existing entries in the
serviceIdToFilePath map (variable serviceId) when scanning files; change the
insertion logic to fail fast on duplicates by checking if serviceId already
exists in serviceIdToFilePath and returning/propagating an error (or logging and
exiting) that includes the conflicting serviceId and both file paths (existing
value and serviceFilePath) so callers of the scanning function can handle the
failure instead of allowing silent overwrite.

In `@docs/osdctl_promote_managedscripts.md`:
- Around line 5-39: Add language identifiers to all fenced code blocks in
docs/osdctl_promote_managedscripts.md to satisfy MD040: mark the command usage
block as "text" (or "bash" if preferred), the example block as "bash", and both
options blocks (Options and Options inherited from parent commands) as "text".
Update the fences surrounding the snippets that contain "osdctl promote
managedscripts [flags]", the example starting with "# Promote managed-scripts
repo", the options list beginning with "--appInterfaceDir", and the inherited
options list beginning with "--as" to include the appropriate language tags.

In `@docs/osdctl_promote_saas.md`:
- Around line 22-30: The fenced code block showing CLI flags in the saas
promotion docs lacks a language identifier which triggers markdownlint MD040;
update the triple-backtick opening fence to include a language (e.g., ```text)
for the block containing the lines starting with "--appInterfaceDir" and ending
with "--serviceId" so the block is treated as plain text and the linter warning
is resolved.
- Line 17: The example for the CLI invocation uses the deprecated alias flag
--serviceName; update the example for the command osdctl promote saas to use the
canonical flag --serviceId instead (replace the --serviceName token with
--serviceId in the example line) so the documentation shows the current,
supported flag.

In `@docs/README.md`:
- Around line 3938-3940: Add a language identifier to the fenced code block that
contains the line "osdctl promote managedscripts [flags]" so markdownlint MD040
is satisfied; modify the opening fence from ``` to ```text (or ```bash) around
the block in README.md so it becomes ```text followed by the command and closing
``` to preserve formatting.

---

Outside diff comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 175-178: The Open(filename) call currently prints the error and
continues, which can pass an invalid file handle into downstream code; update
the error handling at the Open(...) site so that on err you return the error (or
otherwise abort the function) instead of just fmt.Println(err). Locate the
Open(filename) invocation in dt_utils.go and modify the surrounding function to
propagate the error (or call return) immediately when err != nil, ensuring
downstream code does not receive a nil/invalid file handle.
- Around line 142-153: The os.ReadDir calls in the nested directory walk (the
reads that populate items, subitems and subitems2) currently ignore their
returned errors; change each call to capture the error (e.g., items, err :=
os.ReadDir(dir)) and propagate it instead of discarding it—return the error (or
wrap and return) from the enclosing function (the directory-walking function in
dt_utils.go) so failures stop the promotion; do the same for subitems and
subitems2 reads and adjust calling code to handle the returned error.

In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 73-74: The calls to listDynatraceModuleNames currently discard the
returned error and then call os.Exit(0); change both call sites to capture the
error (e.g., err := listDynatraceModuleNames(dynatraceConfig)), check if err !=
nil, print or log the error to stderr (or use the existing logger) and call
os.Exit(1); if no error, then exit with success. This ensures failures from
listDynatraceModuleNames are surfaced instead of being silently ignored.

---

Nitpick comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 108-124: GetModulesNames currently appends to package globals
ModulesSlice and ModulesFilesMap on each call; modify GetModulesNames to
reset/clear ModulesSlice and reinitialize or clear ModulesFilesMap at the start
of the function (before reading dir entries) so repeated calls don't accumulate
stale data; locate the function GetModulesNames and add logic to set
ModulesSlice = nil (or empty slice) and ModulesFilesMap =
make(map[string]string) (or clear existing entries) before populating them.

In `@cmd/promote/utils/service.go`:
- Around line 42-44: The GetName method for type yamlDoc uses an inconsistent
receiver name `s`; change its receiver to `d` to match the other methods on
yamlDoc (e.g., GetFilePath and Save) so all methods use the same receiver
identifier; update the method signature for GetName to use `d` and keep the body
returning d.name.
- Around line 413-419: Remove the extra blank line in the error handling after
calling repo.GetHeadHash so the idiomatic Go pattern is preserved; specifically,
in the block that assigns newHash (the call to repo.GetHeadHash) and the
subsequent if err != nil block, bring the if directly under the call (no blank
line) to keep the standard newHash, err := repo.GetHeadHash / if err != nil
style used elsewhere.
- Around line 296-330: The promote function (resourceTemplatePromotion.promote)
commits each resource independently (uses callbacks.SetTargetHash, service.Save,
service.appInterfaceClone.Commit) which can leave the local branch partially
committed if a later promotion fails; update the caller that loops over
promotions (the code that invokes resourceTemplatePromotion.promote) to track
which promotions succeeded and, on any subsequent error, emit a clear
warning/log that lists the committed resource relPaths and informs the user that
the local branch contains partial commits requiring manual push/revert;
alternatively expose a boolean or error type from promote indicating "committed"
so the caller can build that list before logging the warning.
- Line 21: The exported function ReadYamlDocFromFile returns an unexported type
yamlDoc which prevents external callers from using its value; rename the type to
exported YamlDoc (or change the function to return an exported interface) and
update all references accordingly (including ReadYamlDocFromFile's return type
and any uses in cmd/promote/saas/saas.go). Also standardize method receiver
names on that type: change the GetName receiver from 's' to 'd' to match
GetFilePath and Save so all methods on yamlDoc/YamlDoc use a consistent receiver
(d). Ensure you update any imports/usages and tests to the new exported type
name.

ℹ️ Review info

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between ba2de78 and 90a34b6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (23)
  • cmd/promote/cmd.go
  • cmd/promote/dynatrace/dt_utils.go
  • cmd/promote/dynatrace/dynatrace.go
  • cmd/promote/dynatrace/utils_test.go
  • cmd/promote/git/app_interface.go
  • cmd/promote/git/app_interface_test.go
  • cmd/promote/git/service_repo.go
  • cmd/promote/git/service_repo_test.go
  • cmd/promote/managedscripts/managed_scripts.go
  • cmd/promote/pathutil/pathutil_test.go
  • cmd/promote/pko/pko.go
  • cmd/promote/saas/saas.go
  • cmd/promote/saas/utils.go
  • cmd/promote/saas/utils_test.go
  • cmd/promote/utils/app_interface_clone.go
  • cmd/promote/utils/git_repo.go
  • cmd/promote/utils/service.go
  • cmd/promote/utils/services_registry.go
  • docs/README.md
  • docs/osdctl_promote.md
  • docs/osdctl_promote_managedscripts.md
  • docs/osdctl_promote_saas.md
  • go.mod
💤 Files with no reviewable changes (8)
  • cmd/promote/pathutil/pathutil_test.go
  • cmd/promote/pko/pko.go
  • cmd/promote/saas/utils.go
  • cmd/promote/git/app_interface_test.go
  • cmd/promote/git/service_repo.go
  • cmd/promote/git/app_interface.go
  • cmd/promote/git/service_repo_test.go
  • cmd/promote/saas/utils_test.go

Copy link
Copy Markdown
Member

@MateSaary MateSaary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The promotion command's code finally looks much better 😄 Added a few comments, aside from that;

  • There was decent test coverage for the command before which is now gone; would be nice to have it back but can also be done as a follow-up, we do already have a card for this in backlog: https://issues.redhat.com/browse/SREP-1278

  • it looks like pathutil/pathutil.go isn't being used anywhere now, so we could remove that orphaned code with this PR too

I tried to go through the coderabbit suggestions as well and commented/reacted to them based on whether or not they make sense 😄

@Nikokolas3270
Copy link
Copy Markdown
Contributor Author

Nikokolas3270 commented Feb 26, 2026

The promotion command's code finally looks much better 😄 Added a few comments, aside from that;

Thanks :)

  • There was decent test coverage for the command before which is now gone; would be nice to have it back but can also be done as a follow-up, we do already have a card for this in backlog: https://issues.redhat.com/browse/SREP-1278
  • it looks like pathutil/pathutil.go isn't being used anywhere now, so we could remove that orphaned code with this PR too

True, I am still working on the unit-tests, no worries. I just wanted to have people a first look on that finally quite big change.

I tried to go through the coderabbit suggestions as well and commented/reacted to them based on whether or not they make sense 😄

Thanks! I will TAL as well

This also contains the following changes:
- 'pko' promote subcommand removed as it was no longer applying to any saas file
- '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd
- Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as  'gopkg.in/yaml.v3' is no longer supported
- Now interacting with Git repo with 'github.com/go-git/go-git/v5' lib rather than running shell commands
- Code has been factorized
@Nikokolas3270 Nikokolas3270 marked this pull request as ready for review March 27, 2026 17:56
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 27, 2026
@openshift-ci openshift-ci bot requested review from petrkotas and typeid March 27, 2026 17:57
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Mar 27, 2026

@Nikokolas3270: This pull request references SREP-1107 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This also contains the following changes:

  • 'pko' promote subcommand removed as it was no longer applying to any saas file
  • '--osd' and '--hcp' options removed as a there is no service which could be both hcp and osd
  • Now exclusively using 'sigs.k8s.io/kustomize/kyaml' as both a YAML parser & marshaller as 'gopkg.in/yaml.v3' is no longer supported
  • Now interacting with Git repo with 'github.com/go-git/go-git/v6' lib rather than running shell commands
  • Code has been factorized

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 27, 2026

@Nikokolas3270: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/verify-docs b040d1e link true /test verify-docs
ci/prow/lint b040d1e link true /test lint

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/promote/dynatrace/dt_utils.go (1)

137-166: ⚠️ Potential issue | 🟡 Minor

Errors from os.ReadDir are silently ignored.

Lines 140 and 145 discard errors from os.ReadDir. This could mask permission issues or missing directories, leading to confusing behavior.

🛡️ Proposed fix
 func updatePromotionGitHash(module string, dir string, promotionGitHash string) (string, error) {
 
 	fmt.Printf("Iterating over directory : %s", dir)
-	items, _ := os.ReadDir(dir)
+	items, err := os.ReadDir(dir)
+	if err != nil {
+		return "", fmt.Errorf("failed to read directory '%s': %v", dir, err)
+	}
 	for _, item := range items {
 		fmt.Println("Production tenant: ", item.Name())
 		if item.IsDir() {
 			subDir := filepath.Join(dir, item.Name())
-			subitems, _ := os.ReadDir(subDir)
+			subitems, err := os.ReadDir(subDir)
+			if err != nil {
+				return "", fmt.Errorf("failed to read subdirectory '%s': %v", subDir, err)
+			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dt_utils.go` around lines 137 - 166, The function
updatePromotionGitHash currently ignores errors returned by os.ReadDir (when
listing dir, subDir, and subDir2), which can hide permission or
missing-directory problems; update each os.ReadDir call to capture its error,
check it, and return a wrapped error (including the path and original error)
instead of proceeding silently so callers can fail fast—apply this to the
top-level items, subitems and subitems2 reads in updatePromotionGitHash and
propagate or return errors from updateFileContent as appropriate.
♻️ Duplicate comments (1)
cmd/promote/dynatrace/dynatrace.go (1)

126-131: ⚠️ Potential issue | 🟠 Major

Return the promotion error instead of exiting the process.

os.Exit(1) bypasses Cobra's RunE error path and skips deferred cleanup/tests.

🔧 Proposed fix
-					err = service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash)
-
-					if err != nil {
-						fmt.Printf("Error while promoting service: %v\n", err)
-						os.Exit(1)
-					}
+					if err := service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash); err != nil {
+						return fmt.Errorf("error while promoting service: %w", err)
+					}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dynatrace.go` around lines 126 - 131, The code
currently calls fmt.Printf and os.Exit(1) after service.Promote fails, which
prevents Cobra's RunE error handling and deferred cleanup; change the block in
the function that calls service.Promote(&utils.DefaultPromoteCallbacks{Service:
service}, ops.gitHash) to return the error (or a wrapped error with context)
instead of printing and exiting so the caller/RunE can handle it and deferred
cleanup runs; specifically remove fmt.Printf/os.Exit and return err (or
fmt.Errorf("promote failed: %w", err)) from the surrounding command handler.
🧹 Nitpick comments (3)
cmd/promote/utils/git_repo.go (1)

70-75: Minor: Missing space in warning message.

Line 73 has "Warning:Failed" - should be "Warning: Failed" for consistency with line 40.

✏️ Fix formatting
 func (r *Repo) Cleanup() {
 	err := os.RemoveAll(r.clonePath)
 	if err != nil {
-		fmt.Printf("Warning:Failed to remove clone directory '%s': %v", r.clonePath, err)
+		fmt.Printf("Warning: Failed to remove clone directory '%s': %v", r.clonePath, err)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/git_repo.go` around lines 70 - 75, The warning string in
Repo.Cleanup uses "Warning:Failed" without a space; update the fmt.Printf call
inside Cleanup (function Repo.Cleanup) to insert a space after the colon (i.e.,
"Warning: Failed to remove clone directory '%s': %v") so the message matches the
formatting used elsewhere (referencing r.clonePath and the fmt.Printf call).
cmd/promote/dynatrace/dt_utils.go (1)

25-28: Package-level mutable state can cause test pollution.

ModulesSlice and ModulesFilesMap are package-level variables that GetModulesNames appends to. This can cause issues if tests run in parallel or if the function is called multiple times.

Consider returning fresh slices/maps instead of mutating package-level state, or clearing them at the start of GetModulesNames.

Also applies to: 106-123

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dt_utils.go` around lines 25 - 28, ModulesSlice and
ModulesFilesMap are package-level mutable variables that GetModulesNames appends
to, causing test pollution; change GetModulesNames to use and return fresh local
variables (e.g., local modulesSlice []string and modulesFilesMap
map[string]string) instead of appending to the package-level
ModulesSlice/ModulesFilesMap, and update callers to accept the returned
slice/map, or alternatively clear ModulesSlice and ModulesFilesMap at the start
of GetModulesNames before use; apply the same local/clear fix to the other
similar code block referenced around lines 106-123 (same function or helper) so
no package-level mutation persists across calls/tests.
cmd/promote/utils/app_interface_clone.go (1)

110-128: Consider detecting the default branch dynamically instead of hardcoding master.

The function assumes master is the default branch, which is correct for osdctl. However, many repositories now use main as the default. For robustness, consider detecting the default branch dynamically or making it configurable, so the function doesn't fail if used with repositories that have adopted main.

♻️ Suggested approach
-func (a *AppInterfaceClone) CheckoutNewBranch(branchName string) error {
-	if err := a.workTree.Checkout(&git.CheckoutOptions{Branch: plumbing.NewBranchReferenceName("master")}); err != nil {
-		return fmt.Errorf("failed to checkout master branch in '%s': %v", a.path, err)
+func (a *AppInterfaceClone) CheckoutNewBranch(branchName string, baseBranch string) error {
+	if baseBranch == "" {
+		baseBranch = "master"
+	}
+	if err := a.workTree.Checkout(&git.CheckoutOptions{Branch: plumbing.NewBranchReferenceName(baseBranch)}); err != nil {
+		return fmt.Errorf("failed to checkout %s branch in '%s': %v", baseBranch, a.path, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/app_interface_clone.go` around lines 110 - 128,
CheckoutNewBranch currently hardcodes "master"; change it to detect the
repository's current/default branch instead and use that for the initial
checkout. In CheckoutNewBranch use repo.Head() (or resolve
"refs/remotes/origin/HEAD" if you need remote default) to obtain the default
branch reference name and pass that into workTree.Checkout instead of
plumbing.NewBranchReferenceName("master"), falling back to "master" or "main"
only if detection fails; keep existing logic for branchReference creation,
repo.Reference check, RemoveReference and final Checkout(Create:true).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 93-117: Move the checks that validate non-terraform flags (the
ops.list, ops.component and ops.gitHash logic and the error returns like "--list
cannot be used with --component or --gitHash" and "--component is required
unless --list is used") to run before calling utils.FindAppInterfaceClone and
utils.NewServicesRegistry so missing/invalid flags are caught immediately; i.e.,
validate ops.list/ops.component/ops.gitHash at the top of the function and
return the appropriate errors (or call listServiceIds when ops.list) before
invoking FindAppInterfaceClone or NewServicesRegistry.

In `@cmd/promote/managedscripts/managed_scripts.go`:
- Around line 64-70: The command registered in the Cobra command definition uses
Use: "managedscripts" which does not match the advertised "managed-scripts";
update the Cobra command in managed_scripts.go to either change the Use value to
"managed-scripts" or add an Alias entry including "managedscripts" (or both) so
both forms work; locate the command struct where Use, Short, Args,
DisableAutoGenTag and Example are set and modify the Use/Aliases fields
accordingly to ensure "osdctl promote managed-scripts" is recognized.

In `@cmd/promote/utils/services_registry_test.go`:
- Around line 39-59: The test asserts that GetServicesIds() advertises
"service-4" but GetService("service-4") fails, creating a contract mismatch;
either make GetService succeed for advertised services or stop advertising it.
Fix by updating the test fixture or registry setup used by
services_registry_test.go so the declared service IDs from GetServicesIds()
match retrievable services from GetService() — e.g., ensure the underlying
registry data includes a valid entry/file for "service-4" (so GetService returns
a non-nil service and no error) or remove "service-4" from the expected slice in
the GetServicesIds() assertion; keep references to GetServicesIds and GetService
when applying the change.

---

Outside diff comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 137-166: The function updatePromotionGitHash currently ignores
errors returned by os.ReadDir (when listing dir, subDir, and subDir2), which can
hide permission or missing-directory problems; update each os.ReadDir call to
capture its error, check it, and return a wrapped error (including the path and
original error) instead of proceeding silently so callers can fail fast—apply
this to the top-level items, subitems and subitems2 reads in
updatePromotionGitHash and propagate or return errors from updateFileContent as
appropriate.

---

Duplicate comments:
In `@cmd/promote/dynatrace/dynatrace.go`:
- Around line 126-131: The code currently calls fmt.Printf and os.Exit(1) after
service.Promote fails, which prevents Cobra's RunE error handling and deferred
cleanup; change the block in the function that calls
service.Promote(&utils.DefaultPromoteCallbacks{Service: service}, ops.gitHash)
to return the error (or a wrapped error with context) instead of printing and
exiting so the caller/RunE can handle it and deferred cleanup runs; specifically
remove fmt.Printf/os.Exit and return err (or fmt.Errorf("promote failed: %w",
err)) from the surrounding command handler.

---

Nitpick comments:
In `@cmd/promote/dynatrace/dt_utils.go`:
- Around line 25-28: ModulesSlice and ModulesFilesMap are package-level mutable
variables that GetModulesNames appends to, causing test pollution; change
GetModulesNames to use and return fresh local variables (e.g., local
modulesSlice []string and modulesFilesMap map[string]string) instead of
appending to the package-level ModulesSlice/ModulesFilesMap, and update callers
to accept the returned slice/map, or alternatively clear ModulesSlice and
ModulesFilesMap at the start of GetModulesNames before use; apply the same
local/clear fix to the other similar code block referenced around lines 106-123
(same function or helper) so no package-level mutation persists across
calls/tests.

In `@cmd/promote/utils/app_interface_clone.go`:
- Around line 110-128: CheckoutNewBranch currently hardcodes "master"; change it
to detect the repository's current/default branch instead and use that for the
initial checkout. In CheckoutNewBranch use repo.Head() (or resolve
"refs/remotes/origin/HEAD" if you need remote default) to obtain the default
branch reference name and pass that into workTree.Checkout instead of
plumbing.NewBranchReferenceName("master"), falling back to "master" or "main"
only if detection fails; keep existing logic for branchReference creation,
repo.Reference check, RemoveReference and final Checkout(Create:true).

In `@cmd/promote/utils/git_repo.go`:
- Around line 70-75: The warning string in Repo.Cleanup uses "Warning:Failed"
without a space; update the fmt.Printf call inside Cleanup (function
Repo.Cleanup) to insert a space after the colon (i.e., "Warning: Failed to
remove clone directory '%s': %v") so the message matches the formatting used
elsewhere (referencing r.clonePath and the fmt.Printf call).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 293682b2-48bb-4f01-8afe-c1686d9ba4c5

📥 Commits

Reviewing files that changed from the base of the PR and between 90a34b6 and b040d1e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (31)
  • cmd/promote/cmd.go
  • cmd/promote/dynatrace/dt_utils.go
  • cmd/promote/dynatrace/dynatrace.go
  • cmd/promote/dynatrace/utils_test.go
  • cmd/promote/git/app_interface.go
  • cmd/promote/git/app_interface_test.go
  • cmd/promote/git/service_repo.go
  • cmd/promote/git/service_repo_test.go
  • cmd/promote/managedscripts/managed_scripts.go
  • cmd/promote/managedscripts/managed_scripts_test.go
  • cmd/promote/pathutil/pathutil.go
  • cmd/promote/pathutil/pathutil_test.go
  • cmd/promote/pko/pko.go
  • cmd/promote/saas/saas.go
  • cmd/promote/saas/saas_test.go
  • cmd/promote/saas/utils.go
  • cmd/promote/saas/utils_test.go
  • cmd/promote/utils/app_interface_clone.go
  • cmd/promote/utils/app_interface_clone_test.go
  • cmd/promote/utils/git_repo.go
  • cmd/promote/utils/service.go
  • cmd/promote/utils/service_test.go
  • cmd/promote/utils/services_registry.go
  • cmd/promote/utils/services_registry_test.go
  • cmd/promote/utils/test_tools.go
  • cmd/promote/utils/utils_test.go
  • docs/README.md
  • docs/osdctl_promote.md
  • docs/osdctl_promote_managedscripts.md
  • docs/osdctl_promote_saas.md
  • go.mod
💤 Files with no reviewable changes (9)
  • cmd/promote/git/service_repo.go
  • cmd/promote/pathutil/pathutil_test.go
  • cmd/promote/pathutil/pathutil.go
  • cmd/promote/git/service_repo_test.go
  • cmd/promote/saas/utils_test.go
  • cmd/promote/git/app_interface_test.go
  • cmd/promote/saas/utils.go
  • cmd/promote/pko/pko.go
  • cmd/promote/git/app_interface.go
✅ Files skipped from review due to trivial changes (3)
  • docs/osdctl_promote_managedscripts.md
  • docs/osdctl_promote.md
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (5)
  • cmd/promote/cmd.go
  • cmd/promote/dynatrace/utils_test.go
  • cmd/promote/utils/services_registry.go
  • docs/osdctl_promote_saas.md
  • docs/README.md

Comment on lines +93 to +117
appInterfaceClone, err := utils.FindAppInterfaceClone(ops.appInterfaceProvidedPath)
if err != nil {
return err
}

servicesRegistry, err := utils.NewServicesRegistry(
appInterfaceClone,
validateDynatraceServiceFilePath,
saasDynatraceDir,
)
if err != nil {
return err
}

if ops.list {
if ops.component != "" || ops.gitHash != "" {
fmt.Printf("Error: --list cannot be used with any other flags\n\n")
cmd.Help()
os.Exit(1)
return errors.New("--list cannot be used with --component or --gitHash")
}
listServiceNames(appInterface)
os.Exit(0)
}

if ops.component == "" {
fmt.Printf("Error: Please provide dynatrace component to promote.\n\n")
fmt.Printf("Please run 'osdctl promote dynatrace --list' to check available dynatrace components for promotion.\n\n")
cmd.Help()
os.Exit(1)
}
err := servicePromotion(appInterface, ops.component, ops.gitHash)
if err != nil {
fmt.Printf("Error while promoting service: %v\n", err)
os.Exit(1)
cmd.SilenceUsage = true

return listServiceIds(servicesRegistry)
} else {
if ops.component == "" {
return errors.New("--component is required unless --list is used")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate the non-terraform flags before clone/registry setup.

With the new bootstrap sitting above the ops.list / ops.component checks, osdctl promote dynatrace --list still prints the old usage banner, and missing-flag calls can fail on repo discovery before returning the real flag error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/dynatrace/dynatrace.go` around lines 93 - 117, Move the checks
that validate non-terraform flags (the ops.list, ops.component and ops.gitHash
logic and the error returns like "--list cannot be used with --component or
--gitHash" and "--component is required unless --list is used") to run before
calling utils.FindAppInterfaceClone and utils.NewServicesRegistry so
missing/invalid flags are caught immediately; i.e., validate
ops.list/ops.component/ops.gitHash at the top of the function and return the
appropriate errors (or call listServiceIds when ops.list) before invoking
FindAppInterfaceClone or NewServicesRegistry.

Comment on lines +64 to +70
Use: "managedscripts",
Short: "Promote https://github.com/openshift/managed-scripts",
Args: cobra.NoArgs,
DisableAutoGenTag: true,
Example: `
# Promote managed-scripts repo
osdctl promote managedscripts --gitHash <git-hash>`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Expose the subcommand as managed-scripts or add it as an alias.

The PR advertises osdctl promote managed-scripts, but Use: "managedscripts" registers a different command name. Anyone following the ticket/release notes will hit an unknown command.

🔧 Proposed fix
 	cmd := &cobra.Command{
-		Use:               "managedscripts",
+		Use:               "managed-scripts",
+		Aliases:           []string{"managedscripts"},
 		Short:             "Promote https://github.com/openshift/managed-scripts",
@@
-		osdctl promote managedscripts --gitHash <git-hash>`,
+		osdctl promote managed-scripts --gitHash <git-hash>`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Use: "managedscripts",
Short: "Promote https://github.com/openshift/managed-scripts",
Args: cobra.NoArgs,
DisableAutoGenTag: true,
Example: `
# Promote managed-scripts repo
osdctl promote managedscripts --gitHash <git-hash>`,
Use: "managed-scripts",
Aliases: []string{"managedscripts"},
Short: "Promote https://github.com/openshift/managed-scripts",
Args: cobra.NoArgs,
DisableAutoGenTag: true,
Example: `
# Promote managed-scripts repo
osdctl promote managed-scripts --gitHash <git-hash>`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/managedscripts/managed_scripts.go` around lines 64 - 70, The
command registered in the Cobra command definition uses Use: "managedscripts"
which does not match the advertised "managed-scripts"; update the Cobra command
in managed_scripts.go to either change the Use value to "managed-scripts" or add
an Alias entry including "managedscripts" (or both) so both forms work; locate
the command struct where Use, Short, Args, DisableAutoGenTag and Example are set
and modify the Use/Aliases fields accordingly to ensure "osdctl promote
managed-scripts" is recognized.

Comment on lines +39 to +59
It("finds service-1, service-2 and service-4", func() {
serviceIds := servicesRegistry.GetServicesIds()
Expect(serviceIds).To(Equal([]string{"service-1", "service-2", "service-4"}))
})

It("returns a valid service object for service-1 and service-2", func() {
for _, serviceId := range []string{"service-1", "service-2"} {
service, err := servicesRegistry.GetService(serviceId)
Expect(err).ShouldNot(HaveOccurred())
Expect(service).ToNot(BeNil())
Expect(service.GetFilePath()).To(Equal(filepath.Join(data.AppInterfacePath, "data/services/gen-app/cicd/saas/"+serviceId+".yaml")))
}
})

It("fails to return a valid service object for service-3 even if it was listed", func() {
for _, serviceId := range []string{"service-3", "service-4"} {
service, err := servicesRegistry.GetService(serviceId)
Expect(err).Should(HaveOccurred())
Expect(service).To(BeNil())
}
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep GetServicesIds() and GetService() consistent.

These expectations lock in a registry contract where service-4 is advertised by GetServicesIds() but cannot be retrieved by GetService(). The new registry-backed --list flows use the first API and promotion uses the second, so users can be shown a service they cannot promote.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/promote/utils/services_registry_test.go` around lines 39 - 59, The test
asserts that GetServicesIds() advertises "service-4" but GetService("service-4")
fails, creating a contract mismatch; either make GetService succeed for
advertised services or stop advertising it. Fix by updating the test fixture or
registry setup used by services_registry_test.go so the declared service IDs
from GetServicesIds() match retrievable services from GetService() — e.g.,
ensure the underlying registry data includes a valid entry/file for "service-4"
(so GetService returns a non-nil service and no error) or remove "service-4"
from the expected slice in the GetServicesIds() assertion; keep references to
GetServicesIds and GetService when applying the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants